package no.ntnu.idi.deid.decision.validators;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import no.ntnu.idi.deid.config.Constants;
import no.ntnu.idi.deid.documentunits.Token;

public class DateValidator {
		
	private DateValidator() {

	}
	
	public static boolean isValidDate(Token token) {
		String strippedToken = token.getStripped().toLowerCase();
		if (isValidFormatWithoutYear(strippedToken)
				|| isValidFormatWithYear(strippedToken)) {

			String regex = "("+"(0?[1-9]|[12][0-9]|3[01])"+"("+Constants.ALLOWED_DELIMITERS_DATE+")"+")"+"(0?[1-9]|1[012]|"
					+ generateMonthString() + ")("+"("+Constants.ALLOWED_DELIMITERS_DATE+")"+"((19|20)?\\d\\d))?";
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(strippedToken);
			matcher.reset();
			if (matcher.matches()) {

				int day = Integer.parseInt(matcher.group(2));
				int month;
				int year;

				try {
					month = Integer.parseInt(matcher.group(4));
				} catch (NumberFormatException e) {
					month = getMonthNrFromString(matcher.group(4));
				}
				try {
					year = Integer.parseInt(matcher.group(7));
					return isValidDate(day, month, year);
				} catch (NumberFormatException e) {
					return isValidDate(day, month);
				}
			}
		}
		return false;
	}
	
	private static String generateRegex(String day, String delimiter1 ,String month, String delimiter2, String year) {
		return "("+day+")"+"("+delimiter1+")"+"("+month+")"+"("+delimiter2+")"+"("+year+")";
	}
	private static String generateRegex(String day, String delimiter ,String month) {
		return generateRegex(day,delimiter,month,"","");
	}
	private static boolean isValidFormatWithYear(String input) {
		boolean returnValue = false;
		String day = "0?[1-9]|[12][0-9]|3[01]";
		String month = "0?[1-9]|1[012]";
		String year = "((19|20)?(\\d\\d))";
		List<String> regexList = new ArrayList<String>();
		regexList.add(generateRegex(day, "/", month, "/", year));
		regexList.add(generateRegex(day, "\\.", month, "\\.", year));
		regexList.add(generateRegex(day, "/|\\.|-", month, "-", year));
		regexList.add(generateRegex(day, "\\.(\\s)*", generateMonthFullNameString(), "\\s+", year));
		regexList.add(generateRegex(day, "\\.(\\s)*", generateMonthShortNameString(), "(\\.)?(\\s*)", year));
		regexList.add(generateRegex(day, "(\\s)*", generateMonthShortNameString(), "(\\.)?(\\s*)", year));
		regexList.add(generateRegex("0[1-9]|[12][0-9]|3[01]", "(\\s)?","0[1-9]|1[012]", "(\\s)?", year));
		returnValue= checkRegexList(input, regexList);
		return returnValue;
	}
	
	public static boolean isYear(Token token) {		
		String strippedToken = token.getStripped().replaceAll("[^0-9]", "");
		if(strippedToken.length() == 4) {
			return isValidYear(Integer.parseInt(strippedToken), 1900, 2140);
		}
		Pattern pattern = Pattern.compile("\\d{2}");
		Matcher matcher = pattern.matcher(strippedToken);
		return matcher.matches();
	}
	
	public static boolean isValidPNRDateFormat(String input) {
		boolean returnValue = false;
		List<String> regexList = new ArrayList<String>();
		regexList.add(generateRegex("0[1-9]|[12][0-9]|3[01]", "(\\s)?","0[1-9]|1[012]", "(\\s)?", "(\\d\\d)"));
		returnValue = checkRegexList(input, regexList);
		return returnValue;
	}
	
	private static boolean checkRegexList(String input, List<String> regexList) {
		for(String s : regexList) {
			Pattern pattern = Pattern.compile(s);
			Matcher matcher = pattern.matcher(input);
			matcher.reset();
			if( matcher.matches()) {
				return true;
			}
		}
		return false;
	}
	
	private static boolean isValidFormatWithoutYear(String input) {
		boolean returnValue = false;
		List<String> regexList = new ArrayList<String>();
		regexList.add(generateRegex("0?[1-9]|[12][0-9]|3[01]","\\.","0?[1-9]|1[012]"));
		regexList.add(generateRegex("0?[1-9]|[12][0-9]|3[01]","/","0?[1-9]|1[012]"));
		regexList.add(generateRegex("0[1-9]|[12][0-9]|3[01]","\\s*","0[1-9]|1[012]"));
		regexList.add(generateRegex("0[1-9]|[12][0-9]|3[01]","\\.|\\s+",generateMonthString()));
		returnValue = checkRegexList(input,regexList);
		return returnValue;
	}

	//1. Factory som lager Regexer, tar inn delimiter
	//2. Flere metoder som sjekker de forskjellige casene


	public static boolean isValidDate(int day, int month) {
		if (!isValidMonthWith31Days(day, month)) {
			return false; // only 1,3,5,7,8,10,12 has 31 days
		} else if (month == 2) {
			if (day == 30 || day == 31) {
				return false;
			} else {
				return true;
			}
		} else {
			return true;
		}
	}

	public static boolean isValidDate(int day, int month, int year) {
		if (!isValidMonthWith31Days(day, month)) {
			return false; // only 1,3,5,7,8,10,12 has 31 days
		} else if (month == 2) {
			if (checkFebruary(day, year)) {
				return true;
			} else {
				return false;
			}
		} else {
			return true;
		}
	}
	
	public static boolean isValidYear(Token token, int lowerBound, int upperbound) {
		int year = Integer.parseInt(token.getStripped());
		return isValidYear(year, lowerBound, upperbound);
	}
	
	public static boolean isValidYear(int year, int lowerBound, int upperbound) {
		return (year >= lowerBound && year <= upperbound);		
	}

	private static boolean isValidMonthWith31Days(int day, int month) {
		if ((day == 31)
				&& (month == 4 || month == 6 || month == 9 || month == 11)) {
			return false; // only 1,3,5,7,8,10,12 has 31 days
		} else {
			return true;
		}
	}

	private static boolean checkFebruary(int day, int year) {
		if (year % 4 == 0) {
			if (day == 30 || day == 31) {
				return false;
			} else {
				return true;
			}
		} else {
			if (day == 29 || day == 30 || day == 31) {
				return false;
			} else {
				return true;
			}
		}
	}

	private static String generateMonthString() {
		String returnString = "";
		for (Months m : Months.values()) {
			returnString += "|" + m.shortName.toLowerCase() + "|"
					+ m.fullName.toLowerCase();
		}
		returnString = returnString.substring(1, returnString.length());
		return returnString;
	}
	
	private static String generateMonthFullNameString() {
		String returnString = "";
		for (Months m : Months.values()) {
			returnString += "|"	+ m.fullName.toLowerCase();
		}
		returnString = returnString.substring(1, returnString.length());
		return returnString;
	}
	
	private static String generateMonthShortNameString() {
		String returnString = "";
		for (Months m : Months.values()) {
			returnString += "|" + m.shortName.toLowerCase();
		}
		returnString = returnString.substring(1, returnString.length());
		return returnString;
	}
	
	private static int getMonthNrFromString(String month) {
		for (Months m : Months.values()) {
			if(m.fullName.equalsIgnoreCase(month) || m.shortName.equalsIgnoreCase(month)) {
				return m.monthNr;
			}
		}
		return -1;
	}

	private enum Months {
		JANUAR(1, "Januar", "Jan"), FEBRUAR(2, "Februar", "Feb"), MARS(3,
				"Mars", "Mar"), APRIL(4, "April", "Apr"), MAI(5, "Mai", "Mai"), JUNI(
				6, "Juni", "Jun"), JULI(7, "Juli", "Jul"), AUGUST(8, "August",
				"Aug"), SEPTEMBER(9, "September", "Sept"), OKTOBER(10,
				"Oktober", "Okt"), NOVEMBER(11, "November", "Nov"), DESEMBER(
				12, "Desember", "Des");

		public int monthNr;
		public String fullName;
		public String shortName;

		Months(int monthNr, String fulltNavn, String forkortelse) {
			this.monthNr = monthNr;
			this.fullName = fulltNavn;
			this.shortName = forkortelse;
		}
	}
}
	

